﻿/****************
* Authored by Zhuo "Wizcas" Chen
* 2018 © Wubi.fun all rights reserved
*****************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class BlockInput : MonoBehaviour 
{
    static BlockInput _instance;
    public static BlockInput instance { get { return _instance ?? (_instance = FindObjectOfType<BlockInput>()); } }

    public System.Action onDragStart;
    public System.Action<Vector3> onDragging;
    public System.Action<Vector3> onDragEnd;
    
    Block _draggingBlock;
    bool isDragging { get { return _draggingBlock != null; } }

    private void Update()
    {
        if (_draggingBlock == null && Input.GetMouseButtonDown(0))
        {
            OnDragBegin();
        }
        if (_draggingBlock == null) return;
        if (Input.GetMouseButtonUp(0))
        {
            OnDragEnd();
        }
        else if (Input.GetMouseButton(0))
        {
            OnDragging();
        }
    }

    Block RaycastToBlock(Ray ray, bool isFree)
    {
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 30f, LayerMask.GetMask(isFree ? "FreeBlock" : "SettledBlock")))
        {
            return hit.collider.gameObject.GetComponentInParent<Block>();
        }
        return null;
    }

    void OnDragBegin()
    {
        SetDrag(RaycastToBlock(Camera.main.ScreenPointToRay(Input.mousePosition), true));
    }

    void OnDragging()
    {
        BlockStack stack;
        var toPos = ComputeMousePosOnGround(out stack);
        _draggingBlock.MoveOnGround(toPos, stack);
    }

    void OnDragEnd()
    {
        BlockStack stack;
        var toPos = ComputeMousePosOnGround(out stack);
        _draggingBlock.PutOnGround(toPos, stack);
        _draggingBlock = null;
    }

    BlockStack FindRaycastedStackTop(Vector3 fromWorldPos, Vector3 dir)
    {
        var block = RaycastToBlock(new Ray(fromWorldPos, dir), false);
        if (block != null)
        {
            return block.stack;
        }
        return null;
    }

    Vector3 ComputeMousePosOnGround(out BlockStack toStack)
    {
        var cam = Camera.main;
        var camT = cam.transform;
        var wpos = cam.ScreenToWorldPoint(Input.mousePosition + Vector3.forward);
        var f = (wpos - camT.position).normalized;
        
        toStack = FindRaycastedStackTop(wpos, f);
        if (toStack != null) return toStack.topPos;

        // groundPos is wpos plus a vector that towards the XZ plane, which is f, with the magnitude of hitting the XZ plane
        // the magnitude = wpos.y * csc(θ), and θ is the angle between f and Vector3.down
        return wpos + (1 / Vector3.Dot(f, Vector3.down)) * wpos.y * f;
    }

    public void SetDrag(Block block)
    {
        _draggingBlock = block;
        if (_draggingBlock != null) _draggingBlock.BeginMove();
    }
}

[System.Serializable]
public class BlockInputEvent : UnityEvent<Vector3> { }